﻿#include "Logger.h"
#include "XyNet/Common/System.h"

#ifdef XYNET_USE_BOOST_LOG
#	include <boost/log/expressions.hpp>
#	include <boost/log/sources/severity_channel_logger.hpp>
#	include <boost/log/support/date_time.hpp>
#	include <boost/log/trivial.hpp>
#	include <boost/log/utility/setup.hpp>
using namespace boost::log;
#endif

namespace XyNet
{
	XYNET_IMPLEMENT_DEFAULT_OBJECT(Logger);

	IdGenerator Logger::_idGenerator;

	Logger::Logger()
		: _channelId(_idGenerator()), _rotation(false)
		, _severityLevel(Info), _writeToConsole(false)
	{
#ifndef XYNET_HIDE_CONSOLE
		_writeToConsole = true;
#endif

#ifdef XYNET_USE_BOOST_LOG
		// 添加时间、线程等属性
		add_common_attributes();

		// 这里不初始化文件槽，开始写入日志才初始化，这样可以在初始化后设置日志文件名等
#endif
	}

	void Logger::setBaseFileName(const string& baseFileName, bool rotation)
	{
		destoryFileSink();
		_baseFileName = baseFileName;
		_rotation = rotation;
	}

	void Logger::setSeverityLevel(SeverityLevel level)
	{
		_severityLevel = level;
#ifdef XYNET_USE_BOOST_LOG
		if (_sink)
		{
			_sink->set_filter(expressions::attr<Id>("Channel") == _channelId &&
				trivial::severity >= trivial::severity_level(_severityLevel));
		}
#endif
	}

	bool Logger::getWriteToConsole() const
	{
		return _writeToConsole;
	}

	void Logger::setWriteToConsole(bool setting)
	{
		_writeToConsole = setting;
	}

	void Logger::log(const string& message, SeverityLevel level)
	{
#ifdef XYNET_USE_BOOST_LOG
		if (!_sink && !_baseFileName.empty())
			createFileSink();

		// 带有时间显示的输出，只输出到文件
		if (!_baseFileName.empty())
		{
			sources::severity_channel_logger<trivial::severity_level, Id> logger(
				keywords::channel = _channelId);
			BOOST_LOG_SEV(logger, trivial::severity_level(level)) << message;
		}
#else
		if (!_fileStream.is_open() && !_baseFileName.empty())
			createFileSink();

		if (!_baseFileName.empty())
		{
			auto dateTime = chrono::system_clock::now();
			auto t = chrono::system_clock::to_time_t(dateTime);
			auto tm = std::localtime(&t);
			boost::format format("%d-%d-%d %d:%d:%d");
			format % (1900 + tm->tm_year) % (1 + tm->tm_mon) % tm->tm_mday %
				tm->tm_hour % tm->tm_min % tm->tm_sec;
			auto dateString = format.str();

			boost::format outputFormat("[%s] [0x%08x] [%s] %s\n");
			outputFormat % dateString % this_thread::get_id()
				% getSeverityLevelString(level) % message;
			_fileStream << outputFormat.str();
			_fileStream.flush();
		}
#endif

		// 通常debug状态下才向控制台输出，且受限于控制台显示尺寸限制，不显示时间
		if (_writeToConsole)
		{
			boost::format outputFormat("[0x%08x] [%s] %s");
			outputFormat % this_thread::get_id() % getSeverityLevelString(level) % message;
			System::consoleWriteLine(outputFormat.str());
		}
	}

	void Logger::logTrace(const string& message)
	{
		log(message, Trace);
	}

	void Logger::logDebug(const string& message)
	{
		log(message, Debug);
	}

	void Logger::logInfo(const string& message)
	{
		log(message, Info);
	}

	void Logger::logWarning(const string& message)
	{
		log(message, Warning);
	}

	void Logger::logError(const string& message)
	{
		log(message, Error);
	}

	void Logger::logFatal(const string& message)
	{
		log(message, Fatal);
	}

	void Logger::createFileSink()
	{
		// 生成文件名
		auto fileName = _baseFileName;

		// 生成文件路径
		auto filePath = System::getLogPath();

#ifdef XYNET_USE_BOOST_LOG
		if (_rotation)
			filePath /= fileName + "%Y-%m-%d_%H-%M-%S_%N.log";	// 文件不能用符号":"，用"-"代替
		else
			filePath /= fileName + ".log";

		// 移除已有文件槽
		destoryFileSink();

		// 设定日志输出格式
		boost::log::formatter formatter =
			expressions::stream << "[" <<
			expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S") <<
			"] [" << expressions::attr<boost::log::aux::thread::id>("ThreadID") <<
			"] [" << trivial::severity <<
			"] " << expressions::smessage;

		// 生成后端
		_backend = boost::make_shared<BackendType>(
			keywords::file_name = filePath.toLocal(),
			keywords::rotation_size = 10 * 1024 * 1024,
			keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0));
		_backend->auto_flush(true);

		// 生成文件槽
		_sink = boost::make_shared<SinkType>(_backend);
		_sink->set_formatter(formatter);
		_sink->set_filter(expressions::attr<Id>("Channel") == _channelId &&
			trivial::severity >= trivial::severity_level(_severityLevel));

		// 添加新文件槽
		core::get()->add_sink(_sink);
#else
		filePath /= fileName + ".log";
		_fileStream.open(filePath.getBoostPath());
#endif
	}

	void Logger::destoryFileSink()
	{
#ifdef XYNET_USE_BOOST_LOG
		if (_sink)
			core::get()->remove_sink(_sink);
		_sink.reset();
		_backend.reset();
#else
		_fileStream.close();
#endif
	}

	string Logger::getSeverityLevelString(SeverityLevel level) const
	{
		switch (level)
		{
		case Trace:
			return "trace";
		case Debug:
			return "debug";
		case Info:
			return "info";
		case Warning:
			return "warning";
		case Error:
			return "error";
		case Fatal:
			return "fatal";
		default:
			assert(false);
			return "";
		}
	}
}